﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.ComponentModel;
using System.Diagnostics;

namespace RobocopyGUI
{
    /// <summary>
    /// The engine of the rGUI application.
    /// </summary>
    class RobocopyJob
    {
        //variables/enums
        private string source;
        private string target;
        private string files;
        private List<JobParam> parameters = new List<JobParam>();
        private StringBuilder command = new StringBuilder();
        private Param[] paramReference = new Param[115];
        private Timer updateTimer = new Timer();
        private BackgroundWorker commandThread = new BackgroundWorker();
        public enum ParamType
        {
            Switch = 1,
            SingleValue = 2,
            MultiValue = 3,
            Flag = 4
        }
        
        //subclasses
        public class Param
        {
            public string Syntax { get; set; }
            public string Description { get; set; }
            public ParamType ParamType { get; set; }
            public int Position { get; set; }
        }
        public class JobParam : Param
        {
            public string[] Values { get; set; }
        }

        //Properties
        //Read-write
        public string Source
        {
            get { return source; }
            set { source = value; updateTimer.Stop(); updateTimer.Start(); }
        }
        public string Target
        {
            get { return target; }
            set { target = value; updateTimer.Stop(); updateTimer.Start(); }
        }
        public string Files
        {
            get { return files; }
            set { files = value; updateTimer.Stop(); updateTimer.Start(); }
        }
        //Read-only
        public string Command { get {return command.ToString();} }
        public Param[] ParamReference { get { return paramReference; } }
        public List<JobParam> JobParameters { get {return parameters;}}

        //events
        public delegate void CommandChangedHandler(RobocopyJob sender, EventArgs e);
        public event CommandChangedHandler CommandChanged;
        public delegate void CommandCompletedHandler(object sender, RunWorkerCompletedEventArgs e);
        public event CommandCompletedHandler CommandCompleted;

        //Methods
        //Public
        //Constructor
        public RobocopyJob(object UpdateControl)
        {
            updateTimer.Interval = 2000;
            updateTimer.AutoReset = false;
            updateTimer.Elapsed += new ElapsedEventHandler(updateTimer_Elapsed);
            updateTimer.SynchronizingObject = (System.ComponentModel.ISynchronizeInvoke) UpdateControl;
            commandThread.DoWork += new DoWorkEventHandler(commandThread_DoWork);
            commandThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(commandThread_RunWorkerCompleted);
            for (int index = 0; index <= paramReference.GetUpperBound(0); index++)
            {
                paramReference[index] = new Param();
            }

            #region Parameter Reference declaration
            paramReference[0].Syntax = "/E";
            paramReference[0].Description = "Copy Subdirectories, but not empty ones.";
            paramReference[0].ParamType = ParamType.Switch;
            paramReference[0].Position = 1;

            paramReference[1].Syntax = "/S";
            paramReference[1].Description = "Copy subdirectories, including empty ones.";
            paramReference[1].ParamType = ParamType.Switch;
            paramReference[1].Position = 2;

            paramReference[2].Syntax = "/LEV:";
            paramReference[2].Description = "Only copy the top given number of levels of the source directory tree.";
            paramReference[2].ParamType = ParamType.SingleValue;
            paramReference[2].Position = 3;

            paramReference[3].Syntax = "/Z";
            paramReference[3].Description = "Copy files in restartable mode.";
            paramReference[3].ParamType = ParamType.Switch;
            paramReference[3].Position = 4;

            paramReference[4].Syntax = "/B";
            paramReference[4].Description = "Copy files in backup mode.";
            paramReference[4].ParamType = ParamType.Switch;
            paramReference[4].Position = 5;

            paramReference[5].Syntax = "/ZB";
            paramReference[5].Description = "Use restartable mode; if access denied use Backup mode.";
            paramReference[5].ParamType = ParamType.Switch;
            paramReference[5].Position = 6;

            paramReference[6].Syntax = "/EFSRAW";
            paramReference[6].Description = "Copy all encrypted files in EFS RAW mode.";
            paramReference[6].ParamType = ParamType.Switch;
            paramReference[6].Position = 7;

            paramReference[7].Syntax = "/COPY:";
            paramReference[7].Description = "Specify what to copy for files.";
            paramReference[7].ParamType = ParamType.SingleValue;
            paramReference[7].Position = 8;
                    
            paramReference[8].Syntax = "/DCOPY:T";
            paramReference[8].Description = "Copy directory timestamps.";
            paramReference[8].ParamType = ParamType.Switch;
            paramReference[8].Position = 9;
                    
            paramReference[9].Syntax = "/SEC";
            paramReference[9].Description = "Copy files with security (equivalent to /COPY:DATS).";
            paramReference[9].ParamType = ParamType.Switch;
            paramReference[9].Position = 10;
                    
            paramReference[10].Syntax = "/COPYALL";
            paramReference[10].Description = "Copy all file info (equivalent to /COPY:DATSOU).";
            paramReference[10].ParamType = ParamType.Switch;
            paramReference[10].Position = 11;
                    
            paramReference[11].Syntax = "/NOCOPY";
            paramReference[11].Description = "Copy no file info (useful with /PURGE).";
            paramReference[11].ParamType = ParamType.Switch;
            paramReference[11].Position = 12;
                    
            paramReference[12].Syntax = "/SECFIX";
            paramReference[12].Description = "Fix file security on all files, even skipped files.";
            paramReference[12].ParamType = ParamType.Switch;
            paramReference[12].Position = 13;
                    
            paramReference[13].Syntax = "/TIMFIX";
            paramReference[13].Description = "Fix file times on all files, even skipped files.";
            paramReference[13].ParamType = ParamType.Switch;
            paramReference[13].Position = 14;
                   
            paramReference[14].Syntax = "/PURGE";
            paramReference[14].Description = "Delete destination files/dirs that no longer exist in source.";
            paramReference[14].ParamType = ParamType.Switch;
            paramReference[14].Position = 15;
                    
            paramReference[15].Syntax = "/MIR";
            paramReference[15].Description = "Mirror a directory tree (equivalent to /E plus /PURGE).";
            paramReference[15].ParamType = ParamType.Switch;
            paramReference[15].Position = 16;
                    
            paramReference[16].Syntax = "/MOV";
            paramReference[16].Description = "Move files (delete from source after copying).";
            paramReference[16].ParamType = ParamType.Switch;
            paramReference[16].Position = 17;
                    
            paramReference[17].Syntax = "/MOVE";
            paramReference[17].Description = "Move files AND dirs (delete from source after copying).";
            paramReference[17].ParamType = ParamType.Switch;
            paramReference[17].Position = 18;
                    
            paramReference[18].Syntax = "/A+:";
            paramReference[18].Description = "Add the given attributes to copied files.";
            paramReference[18].ParamType = ParamType.SingleValue;
            paramReference[18].Position = 19;
                   
            paramReference[19].Syntax = "/A-:";
            paramReference[19].Description = "Remove the given attributes from copied files.";
            paramReference[19].ParamType = ParamType.SingleValue;
            paramReference[19].Position = 20;
                    
            paramReference[20].Syntax = "/CREATE";
            paramReference[20].Description = "Create directory tree and zero-length files only.";
            paramReference[20].ParamType = ParamType.Switch;
            paramReference[20].Position = 21;
                    
            paramReference[21].Syntax = "/FAT";
            paramReference[21].Description = "Create destination files using 8.3 FAT file names only.";
            paramReference[21].ParamType = ParamType.Switch;
            paramReference[21].Position = 22;
                    
            paramReference[22].Syntax = "/256";
            paramReference[22].Description = "Turn off very long path (> 256 characters) support.";
            paramReference[22].ParamType = ParamType.Switch;
            paramReference[22].Position = 23;
                   
            paramReference[23].Syntax = "/MON:";
            paramReference[23].Description = "Monitor source; run again when more than the given number of changes seen.";
            paramReference[23].ParamType = ParamType.SingleValue;
            paramReference[23].Position = 24;
                   
            paramReference[24].Syntax = "/MOT:";
            paramReference[24].Description = "Monitor source; run again in the given number of minutes time, if changed.";
            paramReference[24].ParamType = ParamType.SingleValue;
            paramReference[24].Position = 25;
                    
            paramReference[25].Syntax = "/RH:";
            paramReference[25].Description = "Run Hours - times when new copies may be started.";
            paramReference[25].ParamType = ParamType.SingleValue;
            paramReference[25].Position = 26;
                    
            paramReference[26].Syntax = "/PF";
            paramReference[26].Description = "Check run hours on a per file (not per pass) basis.";
            paramReference[26].ParamType = ParamType.Switch;
            paramReference[26].Position = 27;
                    
            paramReference[27].Syntax = "/IPG:";
            paramReference[27].Description = "Inter-Packet Gap (ms), to free bandwidth on slow lines.";
            paramReference[27].ParamType = ParamType.SingleValue;
            paramReference[27].Position = 28;
                    
            paramReference[28].Syntax = "/SL";
            paramReference[28].Description = "Copy symbolic links versus the target.";
            paramReference[28].ParamType = ParamType.Switch;
            paramReference[28].Position = 29;
                    
            paramReference[29].Syntax = "/MT:";
            paramReference[29].Description = "Do multi-threaded copies with the given number of threads.";
            paramReference[29].ParamType = ParamType.SingleValue;
            paramReference[29].Position = 30;
                    
            paramReference[30].Syntax = "/A";
            paramReference[30].Description = "Copy only files with the archive attribute set.";
            paramReference[30].ParamType = ParamType.Switch;
            paramReference[30].Position = 31;
                   
            paramReference[31].Syntax = "/M";
            paramReference[31].Description = "Copy only files with the Archive attribute and reset it.";
            paramReference[31].ParamType = ParamType.Switch;
            paramReference[31].Position = 32;
                    
            paramReference[32].Syntax = "/IA:";
            paramReference[32].Description = "Include only files with any of the given attributes set.";
            paramReference[32].ParamType = ParamType.SingleValue;
            paramReference[32].Position = 33;
                    
            paramReference[33].Syntax = "/XA:";
            paramReference[33].Description = "Exclude files with any of the given attributes set.";
            paramReference[33].ParamType = ParamType.SingleValue;
            paramReference[33].Position = 34;
                    
            paramReference[34].Syntax = "/XF";
            paramReference[34].Description = "Exclude files matching given names/paths/wildcards.";
            paramReference[34].ParamType = ParamType.MultiValue;
            paramReference[34].Position = 35;
                    
            paramReference[35].Syntax = "/XD";
            paramReference[35].Description = "Exclude Directories matching given names/paths.";
            paramReference[35].ParamType = ParamType.MultiValue;
            paramReference[35].Position = 36;
                    
            paramReference[36].Syntax = "/XC";
            paramReference[36].Description = "Exclude changed files.";
            paramReference[36].ParamType = ParamType.Switch;
            paramReference[36].Position = 37;
                    
            paramReference[37].Syntax = "/XN";
            paramReference[37].Description = "Exclude newer files.";
            paramReference[37].ParamType = ParamType.Switch;
            paramReference[37].Position = 38;
                    
            paramReference[38].Syntax = "/XO";
            paramReference[38].Description = "Exclude older files.";
            paramReference[38].ParamType = ParamType.Switch;
            paramReference[38].Position = 39;
                    
            paramReference[39].Syntax = "/XX";
            paramReference[39].Description = "Exclude extra files.";
            paramReference[39].ParamType = ParamType.Switch;
            paramReference[39].Position = 40;
                    
            paramReference[40].Syntax = "/XL";
            paramReference[40].Description = "Exclude lonely files.";
            paramReference[40].ParamType = ParamType.Switch;
            paramReference[40].Position = 41;
                    
            paramReference[41].Syntax = "/IS";
            paramReference[41].Description = "Include same files.";
            paramReference[41].ParamType = ParamType.Switch;
            paramReference[41].Position = 42;
                    
            paramReference[42].Syntax = "/IT";
            paramReference[42].Description = "Include tweaked files.";
            paramReference[42].ParamType = ParamType.Switch;
            paramReference[42].Position = 43;
                    
            paramReference[43].Syntax = "/MAX:";
            paramReference[43].Description = "Maximum file size - exclude files bigger than the given number of bytes.";
            paramReference[43].ParamType = ParamType.SingleValue;
            paramReference[43].Position = 44;
                    
            paramReference[44].Syntax = "/MIN:";
            paramReference[44].Description = "Minimum file size - exclude files smaller than the given number of bytes.";
            paramReference[44].ParamType = ParamType.SingleValue;
            paramReference[44].Position = 45;
                    
            paramReference[45].Syntax = "/MAXAGE:";
            paramReference[45].Description = "Maximum file age - exclude files older than the given date or days.";
            paramReference[45].ParamType = ParamType.SingleValue;
            paramReference[45].Position = 46;
                    
            paramReference[46].Syntax = "/MINAGE:";
            paramReference[46].Description = "Minimum file age - exclude files newer than the given date or days.";
            paramReference[46].ParamType = ParamType.SingleValue;
            paramReference[46].Position = 47;
                    
            paramReference[47].Syntax = "/MAXLAD:";
            paramReference[47].Description = "Maximum last access date - exclude files unused since the given date.";
            paramReference[47].ParamType = ParamType.SingleValue;
            paramReference[47].Position = 48;
                    
            paramReference[48].Syntax = "/MINLAD:";
            paramReference[48].Description = "Minimum last access date - exclude files used since the given date.";
            paramReference[48].ParamType = ParamType.SingleValue;
            paramReference[48].Position = 49;
                    
            paramReference[49].Syntax = "/XJ";
            paramReference[49].Description = "Exclude junction points.";
            paramReference[49].ParamType = ParamType.Switch;
            paramReference[49].Position = 50;
                    
            paramReference[50].Syntax = "/FFT";
            paramReference[50].Description = "Assume FAT File times (2-second granularity).";
            paramReference[50].ParamType = ParamType.Switch;
            paramReference[50].Position = 51;
                    
            paramReference[51].Syntax = "/DST";
            paramReference[51].Description = "Compensate for one-hour DST time differences.";
            paramReference[51].ParamType = ParamType.Switch;
            paramReference[51].Position = 52;
                    
            paramReference[52].Syntax = "/XJD";
            paramReference[52].Description = "Exclude junction points for directories.";
            paramReference[52].ParamType = ParamType.Switch;
            paramReference[52].Position = 53;
                   
            paramReference[53].Syntax = "/XJF";
            paramReference[53].Description = "Exclude junction points for files.";
            paramReference[53].ParamType = ParamType.Switch;
            paramReference[53].Position = 54;
                    
            paramReference[54].Syntax = "/R:";
            paramReference[54].Description = "Number of retries on failed copies.";
            paramReference[54].ParamType = ParamType.SingleValue;
            paramReference[54].Position = 55;
                    
            paramReference[55].Syntax = "/W:";
            paramReference[55].Description = "Wait time between retries.";
            paramReference[55].ParamType = ParamType.SingleValue;
            paramReference[55].Position = 56;
                    
            paramReference[56].Syntax = "/REG";
            paramReference[56].Description = "Save /R:n and /W:n in the Registry as default settings.";
            paramReference[56].ParamType = ParamType.Switch;
            paramReference[56].Position = 57;
                    
            paramReference[57].Syntax = "/TBD";
            paramReference[57].Description = "Wait for sharenames to be defined (retry error 67).";
            paramReference[57].ParamType = ParamType.Switch;
            paramReference[57].Position = 58;
                    
            paramReference[58].Syntax = "/L";
            paramReference[58].Description = "List only - don't copy, timestamp or delete any files.";
            paramReference[58].ParamType = ParamType.Switch;
            paramReference[58].Position = 59;
                    
            paramReference[59].Syntax = "/X";
            paramReference[59].Description = "Report all extra files, not just those selected.";
            paramReference[59].ParamType = ParamType.Switch;
            paramReference[59].Position = 60;
                    
            paramReference[60].Syntax = "/V";
            paramReference[60].Description = "Produce verbose output, showing skipped files.";
            paramReference[60].ParamType = ParamType.Switch;
            paramReference[60].Position = 61;
                   
            paramReference[61].Syntax = "/TS";
            paramReference[61].Description = "Include source file time stamps in the output.";
            paramReference[61].ParamType = ParamType.Switch;
            paramReference[61].Position = 62;
                    
            paramReference[62].Syntax = "/FP";
            paramReference[62].Description = "Include full pathname of files in the output.";
            paramReference[62].ParamType = ParamType.Switch;
            paramReference[62].Position = 63;
                    
            paramReference[63].Syntax = "/BYTES";
            paramReference[63].Description = "Print sizes as bytes.";
            paramReference[63].ParamType = ParamType.Switch;
            paramReference[63].Position = 64;
                    
            paramReference[64].Syntax = "/BYTES";
            paramReference[64].Description = "Print sizes as bytes.";
            paramReference[64].ParamType = ParamType.Switch;
            paramReference[64].Position = 65;
                   
            paramReference[65].Syntax = "/NS";
            paramReference[65].Description = "No size - don't log file sizes.";
            paramReference[65].ParamType = ParamType.Switch;
            paramReference[65].Position = 66;
                   
            paramReference[66].Syntax = "/NC";
            paramReference[66].Description = "No class - don't log file classes.";
            paramReference[66].ParamType = ParamType.Switch;
            paramReference[66].Position = 67;
                    
            paramReference[67].Syntax = "/NFL";
            paramReference[67].Description = "No file list - don't log file names.";
            paramReference[67].ParamType = ParamType.Switch;
            paramReference[67].Position = 68;
                   
            paramReference[68].Syntax = "/NDL";
            paramReference[68].Description = "No directory list - don't log directory names.";
            paramReference[68].ParamType = ParamType.Switch;
            paramReference[68].Position = 69;
                    
            paramReference[69].Syntax = "/NP";
            paramReference[69].Description = "No progress - don't display percentage copied.";
            paramReference[69].ParamType = ParamType.Switch;
            paramReference[69].Position = 70;
                   
            paramReference[70].Syntax = "/ETA";
            paramReference[70].Description = "show estimated time of arrival of copied files.";
            paramReference[70].ParamType = ParamType.Switch;
            paramReference[70].Position = 71;
                    
            paramReference[71].Syntax = "/LOG:";
            paramReference[71].Description = "Output status to the given log file (overwrite existing log).";
            paramReference[71].ParamType = ParamType.SingleValue;
            paramReference[71].Position = 72;
                    
            paramReference[72].Syntax = "/LOG+:";
            paramReference[72].Description = "Output status to the given log file (append to existing log).";
            paramReference[72].ParamType = ParamType.SingleValue;
            paramReference[72].Position = 73;
                    
            paramReference[73].Syntax = "/UNILOG:";
            paramReference[73].Description = "Output status to the given log file as Unicode (overwrite existing log).";
            paramReference[73].ParamType = ParamType.SingleValue;
            paramReference[73].Position = 74;
                   
            paramReference[74].Syntax = "/UNILOG+:";
            paramReference[74].Description = "Output status to the given log file as Unicode (append to existing log).";
            paramReference[74].ParamType = ParamType.SingleValue;
            paramReference[74].Position = 75;
                    
            paramReference[75].Syntax = "/TEE";
            paramReference[75].Description = "Output to console window, as well as the log file.";
            paramReference[75].ParamType = ParamType.Switch;
            paramReference[75].Position = 76;
                   
            paramReference[76].Syntax = "/NJH";
            paramReference[76].Description = "Do not print the job header.";
            paramReference[76].ParamType = ParamType.Switch;
            paramReference[76].Position = 77;
                    
            paramReference[77].Syntax = "/NJS";
            paramReference[77].Description = "Do no print the job summary.";
            paramReference[77].ParamType = ParamType.Switch;
            paramReference[77].Position = 78;
                    
            paramReference[78].Syntax = "/UNICODE";
            paramReference[78].Description = "Output status as Unicode.";
            paramReference[78].ParamType = ParamType.Switch;
            paramReference[78].Position = 79;
                    
            paramReference[79].Syntax = "/JOB:";
            paramReference[79].Description = "take parameters from the given job file.";
            paramReference[79].ParamType = ParamType.SingleValue;
            paramReference[79].Position = 80;
                    
            paramReference[80].Syntax = "/SAVE:";
            paramReference[80].Description = "Save parameters to the given job file.";
            paramReference[80].ParamType = ParamType.SingleValue;
            paramReference[80].Position = 81;
                    
            paramReference[81].Syntax = "/QUIT";
            paramReference[81].Description = "Quit after processing command line (to view parameters).";
            paramReference[81].ParamType = ParamType.Switch;
            paramReference[81].Position = 82;
                    
            paramReference[82].Syntax = "/NOSD";
            paramReference[82].Description = "No source directory is specified.";
            paramReference[82].ParamType = ParamType.Switch;
            paramReference[82].Position = 83;
                    
            paramReference[83].Syntax = "/NODD";
            paramReference[83].Description = "No destination directory is specified.";
            paramReference[83].ParamType = ParamType.Switch;
            paramReference[83].Position = 84;
                    
            paramReference[84].Syntax = "/IF";
            paramReference[84].Description = "Include the following files.";
            paramReference[84].ParamType = ParamType.MultiValue;
            paramReference[84].Position = 85;

            paramReference[100].Syntax = "r";
            paramReference[100].Description = "Flag READONLY files.";
            paramReference[100].ParamType = ParamType.Flag;
            paramReference[100].Position = -1;

            paramReference[101].Syntax = "a";
            paramReference[101].Description = "Flag ARCHIVE files.";
            paramReference[101].ParamType = ParamType.Flag;
            paramReference[101].Position = -2;

            paramReference[102].Syntax = "s";
            paramReference[102].Description = "Flag SYSTEM files.";
            paramReference[102].ParamType = ParamType.Flag;
            paramReference[102].Position = -3;

            paramReference[103].Syntax = "h";
            paramReference[103].Description = "Flag HIDDEN files.";
            paramReference[103].ParamType = ParamType.Flag;
            paramReference[103].Position = -4;

            paramReference[104].Syntax = "c";
            paramReference[104].Description = "Flag COMPRESSED files.";
            paramReference[104].ParamType = ParamType.Flag;
            paramReference[104].Position = -5;

            paramReference[105].Syntax = "n";
            paramReference[105].Description = "Flag NOT CONTENT INDEXED files.";
            paramReference[105].ParamType = ParamType.Flag;
            paramReference[105].Position = -6;

            paramReference[106].Syntax = "e";
            paramReference[106].Description = "Flag ENCRYPTED files.";
            paramReference[106].ParamType = ParamType.Flag;
            paramReference[106].Position = -7;

            paramReference[107].Syntax = "t";
            paramReference[107].Description = "Flag TEMPORARY files.";
            paramReference[107].ParamType = ParamType.Flag;
            paramReference[107].Position = -8;

            paramReference[108].Syntax = "o";
            paramReference[108].Description = "Flag OFFLINE files.";
            paramReference[108].ParamType = ParamType.Flag;
            paramReference[108].Position = -9;

            paramReference[109].Syntax = "D";
            paramReference[109].Description = "Copy data.";
            paramReference[109].ParamType = ParamType.Flag;
            paramReference[109].Position = -4;

            paramReference[110].Syntax = "A";
            paramReference[110].Description = "Copy attributes.";
            paramReference[110].ParamType = ParamType.Flag;
            paramReference[110].Position = -5;

            paramReference[111].Syntax = "T";
            paramReference[111].Description = "Copy timestamps.";
            paramReference[111].ParamType = ParamType.Flag;
            paramReference[111].Position = -6;

            paramReference[112].Syntax = "S";
            paramReference[112].Description = "Copy security (NTFS ACLs).";
            paramReference[112].ParamType = ParamType.Flag;
            paramReference[112].Position = -7;

            paramReference[113].Syntax = "O";
            paramReference[113].Description = "Copy owner info.";
            paramReference[113].ParamType = ParamType.Flag;
            paramReference[113].Position = -8;

            paramReference[114].Syntax = "U";
            paramReference[114].Description = "Copy auditing info.";
            paramReference[114].ParamType = ParamType.Flag;
            paramReference[114].Position = -9;
            #endregion
        }

        public void ToggleSwitch(string syntax)
        {
            int existingIndex = parameters.FindIndex(x => x.Syntax == syntax);
            if (existingIndex > -1) { RemoveJobParameter(parameters[existingIndex]); }
            else { AddJobParameter(syntax, new string[1]); }
        }
        
        public void AddJobParameter(Param param, string[] values)
        {
            int existingIndex = parameters.FindIndex(x => x.Syntax == param.Syntax);
            if (existingIndex > -1)
            {
                parameters[existingIndex].Values = values;
            } 
            else
            {
                JobParam newParam = new JobParam();
                newParam.Syntax = param.Syntax;
                newParam.Description = param.Description;
                newParam.ParamType = param.ParamType;
                newParam.Position = param.Position;
                newParam.Values = values;
                parameters.Add(newParam);
            }
            updateTimer.Stop(); updateTimer.Start();
        }

        public void AddJobParameter(string syntax, string[] values)
        {
            Param newParam = paramReference.First(x => x.Syntax == syntax);
            AddJobParameter(newParam, values);
        }

        public void RemoveJobParameter(JobParam param)
        {
            parameters.Remove(param);
            updateTimer.Stop(); updateTimer.Start();
        }

        public void RemoveJobParameter(string syntax)
        {
            JobParam param = parameters.SingleOrDefault(x => x.Syntax == syntax);
            if (param != null) { RemoveJobParameter(param); }
        }

        public void RunJob()
        {
            commandThread.RunWorkerAsync();
        }

        //Private
        private void buildCommand()
        {
            if (source == null | target == null) { return; }
            command.Clear();
            command.Append("robocopy");
            command.Append(source.IndexOf(' ') > -1 ? " \"" + source + '"' : " " + source);
            command.Append(target.IndexOf(' ') > -1 ? " \"" + target + '"' : " " + target);
            command.Append(Files != null ? " " + Files : "");
            // sort the parameters according to predetermined position
            // i use a lambda expression here as a shortcut instead of writing a seperate class
            // that implements IComparer, but i don't fully understand lambda expressions yet
            parameters.Sort(new Comparison<JobParam>((x, y) => x.Position.CompareTo(y.Position)));
            foreach (JobParam param in parameters)
            {
                command.Append(" " + param.Syntax);
                switch (param.ParamType)
                {
                    case ParamType.MultiValue:
                        command.Append(" " + string.Join(" ", param.Values));
                        break;
                    case ParamType.SingleValue:
                        command.Append(param.Values[0]);
                        break;
                }
            }
            if (CommandChanged != null) { CommandChanged(this, new EventArgs()); }
        }

        private void updateTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            buildCommand();
        }

        private void commandThread_DoWork(object sender, DoWorkEventArgs e)
        {
            ProcessStartInfo info = new ProcessStartInfo
            {
                UseShellExecute = false,
                LoadUserProfile = true,
                ErrorDialog = false,
                CreateNoWindow = false,
                WindowStyle = ProcessWindowStyle.Normal,
                RedirectStandardOutput = false,
                //StandardOutputEncoding = Encoding.UTF8,
                RedirectStandardError = false,
                //StandardErrorEncoding = Encoding.UTF8,

                FileName = "cmd.exe",
                Arguments = "/c " + command
            };
            Process shell = new Process();
            shell.StartInfo = info;
            shell.Start();
            shell.WaitForExit();
        }

        void commandThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (CommandCompleted != null) { CommandCompleted(this, e); }
        }
    }
}
